package com.tchhy.provider.net

import com.tchhy.basemodule.common.BaseApplication
import com.tchhy.basemodule.constant.SPConstants
import com.tchhy.basemodule.constant.SPConstants.API_VISION_LOCAL
import com.tchhy.basemodule.ext.Preferences
import com.tchhy.basemodule.utils.SystemUtils
import com.tchhy.provider.AdvertiseConstant
import com.tchhy.provider.URLConstant
import com.tchhy.provider.net.interceptor.CacheNetworkInterceptor
import com.tchhy.provider.utils.Logger
import com.tchhy.provider.utils.PreUtils
import com.tchhy.provider.utils.SHAHelper
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONArray
import org.json.JSONObject
import org.json.JSONTokener
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import java.io.IOException
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit
import kotlin.jvm.Throws

class RetrofitFactory private constructor() {

    private val TAG = javaClass.simpleName

    companion object {
        private lateinit var interceptor: Interceptor

        // by lazy 默认线程安全
        val instance: RetrofitFactory by lazy { RetrofitFactory() }
        var mBuilder: Retrofit.Builder = Retrofit.Builder()
        var retrofit: Retrofit? = null
    }


    init {
        interceptor = Interceptor { chain ->
            var newVersion: String by Preferences(BaseApplication.app, "newVersion", SPConstants.API_VISION_LOCAL)

            var newVersionStr = ""
            if (chain.request().url.toUrl()
                    .toString() == ("${URLConstant.BASE_URL}/api/ca/app/advert/getByType?advertType=1") ||
                chain.request().url.toUrl()
                    .toString() == ("${URLConstant.BASE_URL}/api/ca/app/advert/getByType?advertType=2") ||
                chain.request().url.toUrl()
                    .toString() == ("${URLConstant.BASE_URL}/${URLConstant.GET_ADVERTIZIMENT_BY_CODE}?code=${AdvertiseConstant.ADVERTISE_1}")
            ) {//此时未获取到后台返回的apivision
                newVersionStr = SPConstants.API_VISION_LOCAL
            } else {
                newVersionStr = newVersion
            }
            val random = SHAHelper.getRandomString(10)
            val timestamp = System.currentTimeMillis().toString()
            val appVersion = SystemUtils.getVerName(BaseApplication.app) + "_Android"

            val sign = encrypt(newVersionStr, appVersion, random, timestamp, chain.request())

            val requestBuilder = chain.request().newBuilder().run {
                addHeader("Content-Type", "application/json")
                addHeader("charset", "UTF-8")
                addHeader("apiVersion", newVersionStr)
                addHeader("AV", appVersion)
                addHeader("TS", timestamp)
                addHeader("channel", "ca")
                addHeader("RDM", random)
                addHeader("SN", sign)
            }
            var request = if ((BaseApplication.app as BaseApplication).getToken() != null) {


                requestBuilder.addHeader(
                    "Authentication",
                    (BaseApplication.app as BaseApplication).getToken()!!
                ).build()
            } else {
                requestBuilder.build()
            }



            chain.proceed(request).apply {
                val replaceToken = this.header(
                    "Authentication",
                    (BaseApplication.app as BaseApplication).getToken()
                )
                if (replaceToken != null) {
                    with(BaseApplication.app as BaseApplication) {
                        updateToken(replaceToken)
                    }
                }
            }
        }

        if (PreUtils.servicePath == "") {
            retrofit = mBuilder.run {
                baseUrl(URLConstant.BASE_URL)
                addConverterFactory(BaseGsonConverterFactory.create())
                addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                client(initClient())
            }.build()
        } else {
            retrofit = mBuilder.run {
                baseUrl(PreUtils.servicePath)
                addConverterFactory(BaseGsonConverterFactory.create())
                addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                client(initClient())
            }.build()
        }

    }

    fun initClient() = OkHttpClient.Builder().run {
        //缓存配置
        //cache(Cache(cahceDirect, 1024 * 1024 * 100)) // 100m
        //addInterceptor(CacheInterceptor())
        addNetworkInterceptor(CacheNetworkInterceptor())
        addInterceptor(interceptor)
        // 日志拦截器
        addInterceptor(initLogInterceptor())
        //连接服务时间
        connectTimeout(60, TimeUnit.SECONDS)
        // 读取服务数据的等待时间
        readTimeout(30, TimeUnit.SECONDS)
        // 写数据至服务的时间
        writeTimeout(30, TimeUnit.SECONDS)
        //连接池，每个地址的最大闲置数5个
        connectionPool(ConnectionPool(6, 6, TimeUnit.MINUTES))
        //是否重试
        retryOnConnectionFailure(true)
    }.build()

    private fun initLogInterceptor() =
        HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
            override fun log(message: String) {
                Logger.d("OkHttp", message)
            }

        }).also {
            it.level = HttpLoggingInterceptor.Level.BODY
        }


    /**
     * 构造Api service
     */
    fun <T> createApiService(service: Class<T>): T {
        return retrofit!!.create(service)
    }

    //加密
    @Throws(IOException::class)
    private fun encrypt(
        apiVersion: String,
        appVersion: String,
        random: String,
        timestamp: String,
        request: Request
    ): String {
        val url = request.url
        val encryptList = ArrayList<String>()
        var api = ""
        request.url.pathSegments.forEach {
            api += "/${it}"
        }
        encryptList.add(api)
        Logger.d(TAG, "api=$api")

        encryptList.add(random)
        Logger.d(TAG, "random=$random")

        val token = (BaseApplication.app as BaseApplication).getToken()
        encryptList.add(token ?: "")
        Logger.d(TAG, "token=$token")

        encryptList.add(apiVersion)
        Logger.d(TAG, "apiVersion=$apiVersion")

        encryptList.add(appVersion)
        Logger.d(TAG, "appVersion=$appVersion")

        encryptList.add(timestamp)
        Logger.d(TAG, "timestamp=$timestamp")

        val paramsList = ArrayList<String>()
        url.queryParameterNames.forEach {
            if (!url.queryParameter(it).isNullOrEmpty()) {
                paramsList.add("${it}=${url.queryParameter(it)?.replace("\\/", "/")}")
            }
        }

        //获取请求body，只有@Body 参数的requestBody 才不会为 null
        request.body?.let { requestBody ->
            val buffer = okio.Buffer()
            requestBody.writeTo(buffer)
            requestBody.contentType()?.let { contentType ->
                contentType.charset(Charset.forName("UTF-8"))?.let { charset ->
                    val data = buffer.readString(charset)
                    Logger.d(TAG, "data=$data")
                    try {
                        val tokener = JSONTokener(data).nextValue()
                        if (tokener is JSONObject) {
                            val it = tokener.keys()
                            while (it.hasNext()) {//遍历JSONObject
                                val key = it.next()
                                val value = tokener.get(key)
                                if (value.toString().isNotEmpty()) {
                                    paramsList.add("${key}=${value.toString().replace("\\/", "/")}")
                                }
                            }
                        } else if (tokener is JSONArray) {
                            paramsList.add("_ARRAY=${tokener.toString().replace("\\/", "/")}")
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                }
            }
        }
        paramsList.sortBy {
            it
        }
        Logger.d(TAG, "paramsList=$paramsList")
        var params = ""
        for (index in paramsList.indices) {
            params += paramsList[index]
            if (index != paramsList.size - 1) {
                params += "&"
            }
        }
        Logger.d(TAG, "params=$params")
        encryptList.add(params)
        var encrypt = ""
        for (index in encryptList.indices) {
            encrypt += encryptList[index]
            if (index != encryptList.size - 1) {
                encrypt += ","
            }
        }
        Logger.d(TAG, "encrypt=$encrypt")
        val sign = SHAHelper.getHMACSHA256(encrypt, SHAHelper.KEY)
        Logger.d(TAG, "sign=$sign")

        return sign.trim()
    }
}